﻿using System;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;

namespace Toybox.NicoSharp.Video.MyPage
{
	using Extensions;
	using Utilities;

	public enum TokenType
	{
		Auto,

		GlobalHash,
		CsrfToken,
		NicoAPI,
	}

	public class Token : EventArgs
	{

		#region [Static]

		public const string REGEX_GLOBAL_HASH = "Mypage_globals.hash = '(?<token>.+?)'";
		public const string REGEX_CSRF_TOKEN = "csrf_token = '(?<token>.+?)'";
		public const string REGEX_NICO_API = "NicoAPI.token = \"(?<token>.+?)\";";

		#endregion [Static]


		#region Events

		public event EventHandler<Token> UpdateCompleted;

		protected virtual void OnUpdateCompleted(Token token)
		{
			if (this.UpdateCompleted != null)
				this.UpdateCompleted(this, token);
		}

		#endregion Events


		#region Constructor

		public Token(string url = null, CookieCollection cookies = null)
		{
			if (string.IsNullOrEmpty(url))
			{
				this.Url = NicoUrls.VideoTop + "my/";
			}
			else
			{
				this.Url = url;
			}
			this.Cookies = cookies;

			this.Type = TokenType.Auto;
		}

		#endregion Constructor


		#region Public Members

		public string Url
		{
			get;
			protected set;
		}

		public CookieCollection Cookies
		{
			get { return this._cookies.Coerce(); }
			set { this._cookies = value; }
		}
		private CookieCollection _cookies;


		public TokenType Type
		{
			get;
			set;
		}


		public string Value
		{
			get;
			protected set;
		}

		public string UserId
		{
			get;
			protected set;
		}

		public virtual bool Expired
		{
			get { return Expires < DateTime.UtcNow.GetUnixTime(); }
		}

		public int Expires
		{
			get { return _expires; }
			protected set { _expires = value; }
		}
		private int _expires;

		#endregion Public Members


		#region Public Methods

		public virtual string Update()
		{
			string html = NicoWebClient.Get(this.Url, this.Cookies);

			this.SetToken(html);
			return this.Value;
		}

		public virtual void UpdateAsync()
		{
			var wc = new NicoWebClient();
			wc.DownloadStringCompleted += wc_DownloadStringCompleted;
			wc.Encoding = Encoding.UTF8;
			wc.CookieContainer.Add(this.Cookies);

			wc.DownloadStringAsync(new Uri(this.Url));
		}

		#endregion Public Methods

		#region Protected Methods

		protected virtual void SetToken(string html)
		{
			string token = null;
			switch (this.Type)
			{
				case TokenType.Auto:
					token = this.GetGlobalHash(html);
					if (string.IsNullOrEmpty(token))
						token = this.GetCsrfToken(html);
					if (string.IsNullOrEmpty(token))
						token = this.GetNicoAPI(html);
					break;
				case TokenType.GlobalHash:
					token = this.GetGlobalHash(html);
					break;
				case TokenType.CsrfToken:
					token = this.GetCsrfToken(html);
					break;
				case TokenType.NicoAPI:
					token = this.GetNicoAPI(html);
					break;
			}

			this.Value = token;

			string[] elements = this.Value.Split(new string[] { "-" }, StringSplitOptions.RemoveEmptyEntries);
			if (elements.Length != 3)
			{
				this.Value = null;

				this.UserId = null;
				this.Expires = 0;
				//throw new ArgumentException("token");
			}
			else
			{
				this.UserId = elements[0];
				this.Expires = int.Parse(elements[1]);
			}
		}

		#endregion Protected Methods

		#region Private Methods

		private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
		{
			NicoWebClient wc = sender as NicoWebClient;

			this.SetToken(e.Result);

			wc.Dispose();
			this.OnUpdateCompleted(this);
		}

		private string GetGlobalHash(string html)
		{
			Match match = Regex.Match(html, REGEX_GLOBAL_HASH);
			return match.Success ? match.Groups["token"].Value : null;
		}

		private string GetCsrfToken(string html)
		{
			Match match = Regex.Match(html, REGEX_CSRF_TOKEN);
			return match.Success ? match.Groups["token"].Value : null;
		}

		private string GetNicoAPI(string html)
		{
			Match match = Regex.Match(html, REGEX_NICO_API);
			return match.Success ? match.Groups["token"].Value : null;
		}

		#endregion Private Methods

	}

}
